﻿namespace FFF.Test.Application.AppServices.PublicService.Token
{
    using System;
    using System.Collections.Generic;
    using System.IdentityModel.Tokens.Jwt;
    using System.IO;
    using System.Linq;
    using System.Security.Claims;
    using System.Text;
    using System.Threading.Tasks;
    using Abp.Application.Services;
    using Abp.Auditing;
    using Abp.EntityFrameworkCore;
    using Abp.Runtime.Security;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.IdentityModel.Tokens;
    using Newtonsoft.Json;
    using FFF.Test.Application.Repositories.FW;
    using FFF.Test.DataAccess.Framework;
    using FFF.Test.Entity.FW.View;
    using FFF.Test.Util.Configuration;
    using FFF.Test.Util.Web;

    /// <summary>
    /// Token的管理对象
    /// </summary>
    [YdmApiVersionAttribute(Version = "My")]
    public class TokenAuthService : YdmAbpAppServiceBase, ITokenAuthService
    {
        private readonly IConfiguration _appConfiguration;

        private readonly IFWUserRepository _fWUserRepository;

        /// <summary>
        /// Initializes a new instance of the <see cref="TokenAuthService"/> class.
        /// toke构造方法初始化配置对象
        /// </summary>
        /// <param name="fWUserRepository">用户接口</param>
        /// <param name="configuration">读取配置</param>
        /// <param name="dbContextProvider">db</param>
        public TokenAuthService(
            IConfiguration configuration,
            IFWUserRepository fWUserRepository
        )
        {
            this._appConfiguration = configuration;
            this._fWUserRepository = fWUserRepository;
        }

        /// <summary>
        /// 获取Token的验证，后期所有的程序将从这个入口获取验证
        /// </summary>
        /// <param name="model">验证的model</param>
        /// <returns>验证对象</returns>
        public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)
        {
            var fwUser = await this._fWUserRepository.GetAll().FirstOrDefaultAsync(x => x.UserCode == model.UserNameOrEmailAddress && x.UserPassword == model.Password);

            ViewFWUser viewFwUser = new ViewFWUser();

            if (fwUser != null)
            {
                viewFwUser = await this._fWUserRepository.GetView().FirstAsync(x => x.Id == fwUser.Id);
            }

            string accessToken = this.CreateAccessToken(viewFwUser);
            return new AuthenticateResultModel
            {
                AccessToken = accessToken,
                EncryptedAccessToken = this.GetEncrpyedAccessToken(accessToken),
                ExpireInSeconds = (int)TimeSpan.FromMinutes(Convert.ToDouble(viewFwUser.TokenExpiryMinute == 0 ? 60 : viewFwUser.TokenExpiryMinute)).TotalSeconds - 60
            };
        }

        /// <summary>
        /// Creates the access token.
        /// </summary>
        /// <param name="viewFwUser">The view fw user.</param>
        /// <returns>System.String.</returns>
        /// <remarks>ydm 2020-02-01</remarks>
        public string CreateAccessToken(ViewFWUser viewFwUser)
        {
            TokenAuthConfigurationModel configuration = new TokenAuthConfigurationModel();
            configuration.SecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(this._appConfiguration["Authentication:JwtBearer:SecurityKey"])); // 密钥
            configuration.Issuer = this._appConfiguration["Authentication:JwtBearer:Issuer"]; // JWT的签发主体
            configuration.Audience = this._appConfiguration["Authentication:JwtBearer:Audience"]; // 个JWT的主体，即它的所有人
            configuration.SigningCredentials = new SigningCredentials(configuration.SecurityKey, SecurityAlgorithms.HmacSha256);
            configuration.Expiration = TimeSpan.FromMinutes(Convert.ToDouble(viewFwUser.TokenExpiryMinute == 0 ? 60 : viewFwUser.TokenExpiryMinute)); // 过期时间
            DateTime now = DateTime.UtcNow;

            ClaimsIdentity identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);

            HeadBuInfo headBuInfo = new HeadBuInfo()
            {
                ApplicationGUID = viewFwUser.DefaultApplicationGUID,
                BuGUID = viewFwUser.DefaultBuUnitGUID,
                CompanyGUID = viewFwUser.DefaultCompanyGUID
            };

            List<Claim> claims = identity.Claims.ToList();
            claims.AddRange(new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, viewFwUser.UserIntID.ToString()), // 个JWT的主体，即它的所有人
                new Claim(JwtRegisteredClaimNames.Sid, viewFwUser.Id.ToString()), // 存储用户的ID
                new Claim(JwtRegisteredClaimNames.Website, JsonConvert.SerializeObject(headBuInfo)), // 存储用户的ID
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), // JWT的唯一标识
                new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), // JWT的签发时间
            });

            var tokenRoleStr = viewFwUser.TokenRoles ?? "admin";

            tokenRoleStr.Split(",").ToList().ForEach(x =>
            {
                claims.Add(new Claim(ClaimTypes.Role, x));
            });
            JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
                issuer: configuration.Issuer,
                audience: configuration.Audience,
                claims: claims,
                notBefore: now, // 签发前的时间
                expires: now.Add(configuration.Expiration), // 过期时间
                signingCredentials: configuration.SigningCredentials
            );

            return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
        }

        private string CreateAccessToken(TimeSpan? expiration = null)
        {
            TokenAuthConfigurationModel configuration = new TokenAuthConfigurationModel();
            configuration.SecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(this._appConfiguration["Authentication:JwtBearer:SecurityKey"]));
            configuration.Issuer = this._appConfiguration["Authentication:JwtBearer:Issuer"];
            configuration.Audience = this._appConfiguration["Authentication:JwtBearer:Audience"];
            configuration.SigningCredentials = new SigningCredentials(configuration.SecurityKey, SecurityAlgorithms.HmacSha256);
            configuration.Expiration = TimeSpan.FromDays(365);

            DateTime now = DateTime.UtcNow;
            List<dynamic> list = new List<dynamic>
            {
                new { UserName = "admin", Password = "111111", Role = "Admin" },
                new { UserName = "aaa", Password = "222222", Role = "system" }
            };
            dynamic user = list.First();

            // var identity2 = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
            // identity2.AddClaim(new Claim(ClaimTypes.NameIdentifier, "1"));
            // identity2 = new ClaimsIdentity(new System.Security.Principal.GenericIdentity("admin", "Token"), new Claim[] { });
            ClaimsIdentity identity2 = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
            List<Claim> claims = identity2.Claims.ToList();
            claims.AddRange(new[]
            {
                // new Claim(ClaimTypes.Name, user.UserName),

                // new Claim(ClaimTypes.Role, user.Role),
                new Claim(ClaimTypes.Role, "admin"),
                new Claim(JwtRegisteredClaimNames.Sub, "123"),

                // new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),

                // new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),

                new Claim("EmployeeNumber", "123456", ClaimValueTypes.String, "http://www.cnblogs.com/rohelm")
            });
            JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
                issuer: configuration.Issuer,
                audience: configuration.Audience,
                claims: claims,
                notBefore: now,
                expires: now.Add(expiration ?? configuration.Expiration),
                signingCredentials: configuration.SigningCredentials
            );

            return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
        }

        private string GetEncrpyedAccessToken(string accessToken)
        {
            return SimpleStringCipher.Instance.Encrypt(accessToken, "gsKxGZ012HLL3MI5");
        }
    }

}